package com.zf.nospring.rabbitmq.simple;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;

/**
 * 简单模式：一个生产者和一个消费者，使用默认交换机，所有的队列都会绑定到默认交换机上且routing_key是队列名
 * <p>
 * 向默认交换机发送消息时，用 "" 空字符串标识默认交换机，用 队列名作为路由键，就可以把消息发到对应的队列中了
 */
public class SimpleProducer {
    public static void main(String[] args) throws IOException, TimeoutException {
        // 1、创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 2、设置连接参数
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/rabbitmq-sample-vh");
        // 3、创建连接 Connection，一个连接中可以包含多个通道，通过通道进行消息的读取和发送
        Connection connection = connectionFactory.newConnection();

        // 4、在连接中创建通道 Channel，通过通道向交换机发送消息 或者 从队列中读取消息
        // 通道 Channel 既能读取也能发送
        Channel channel = connection.createChannel();

        // 5、声明交换机，不存在就创建，存在就校验已经存在的参数和声明的参数是否一致，不一致就报错
        // 声明交换机
        /*
           exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map<String, Object> arguments)
           参数：
              exchange:交换机名称
              type:交换机类型
              durable:是否持久化，非持久化的交换机会在rabbitmq重启后消失，而持久化的交换机在rabbitmq重启后依然存在
              autoDelete:自动删除
              internal：如果 true 表示该交换机只能 rabbitmq 内部使用的交换机，我们的客户端无法直接使用，所以一般false，默认是false
              arguments：参数
        */
        // 这里不声明，可以使用默认交换机，空字符串表示默认交换机，所有的队列都会绑定到默认交换机上且routing_key是队列名
        // channel.exchangeDeclare("test", BuiltinExchangeType.DIRECT, true, false, false, null);

        // 6、声明队列，不存在就创建，存在就校验已经存在的参数和声明的参数是否一致，不一致就报错
        /* queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
            参数：
            queue 队列名
            durable 队列是否持久化，一般都要设置持久化，非持久化的队列会在rabbitmq重启后消失，而持久化的队列在rabbitmq重启后依然存在
            exclusive 排他，
                当exclusive = true则表示该队列仅对"首次声明"它的"连接（Connection）可见"，
                    并且在该Connection关闭时connection.close()时自动删除该队列,
                    该Connection中的channel都可以访问，但是其他Connection不行，;
                当 exclusive = false，表示所有Connection中的通道都能访问。且不会在Connection关闭时删除队列。
            autoDelete 自动删除如果autoDelete = true，当所有消费者Connection都与这个队列断开连接时，这个队列会自动删除。
                 但是当生产者刚刚声明了该队列且没有消费者连接消费时，该队列是不会自动删除的。
                 一旦Connection中的channel发生了消费动作那么这个Connection 就是相当于消费者连接了，可以在管理页面看到消费者+1。
            arguments 设置队列的其他一些参数，如 x-message-ttl、x-dead-letter-exchange、x-dead-letter-routing-key 等。
        */
        channel.queueDeclare("hello_base", true, false, false, null);

        // 7、绑定队列和交换机，并指定路由键，如果已存在的绑定的队列、交换机、路由键、参数完全一致则不再重复绑定，否则就创建绑定
        // queueBind(String queue, String exchange, String routingKey, Map<String, Object> arguments)

        // 也可以绑定交换机到交换机，destination:目标交换机，source:原始交换机，routingKey:消息满足该路由键才能发送到目标交换机，arguments:参数
        // 消息从一个交换机到另一个交换机，消息携带的路由键不会改变，如果最后一个交换机没有找到匹配的队列，则消息被丢弃
        // exchangeBind(String destination, String source, String routingKey, Map<String, Object> arguments)

        // 8、发送消息，是向交换机发送消息，由交换机根据路由键将消息发送的绑定的队列上
        // 这里使用 默认交换机，用空字符表示默认交换机，所有的队列都会绑定到默认交换机上且routing_key是队列名
        /*
        basicPublish(String exchange, String routingKey, AMQP.BasicProperties props, byte[] body)
         参数：
            exchange 交换机的名称
            routingKey 路由键
            props 消息的属性，如类型、是否持久化、编码、过期时间等
            body 消息本体

          注意：发送的消息默认是非持久化的，非持久化的消息会在rabbitmq重启后消失，而持久化的消息在rabbitmq重启后依然存在，
                        如果队列没有持久化那么消息就算持久化也依然会在重启后丢失，因为存储消息的队列消失了。
          如果需要持久化，需要设置 AMQP.BasicProperties.deliveryMode 属性的值为 2，也就是第三个参数的 props的 deliveryMode 属性的值为 2
         */
        // 发送非持久化消息，rabbitmq重启后消息丢失
        channel.basicPublish("", "hello_base", null, "你好a1".getBytes(StandardCharsets.UTF_8));
        channel.basicPublish("", "hello_base", null, "你好a2".getBytes(StandardCharsets.UTF_8));

        // 持久化参数
        AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                .deliveryMode(2)
                .build();
        // 发送持久化消息，rabbitmq重启后消依然存在，前提是队列是持久化的
        channel.basicPublish("", "hello_base", properties, "持久化消息".getBytes(StandardCharsets.UTF_8));

        // 关闭通道和连接
        channel.close();
        connection.close();
    }
}
